/*
    This is a set of libraries to assist in OpenGl and Other Applications
    Copyright (C) 2008  The Modular Engine Team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#define CLIENTMODULE
#include "ClientModule.h"

namespace ModularEngine
{
	void cCONNECT::init()
	{
		NEWARG( C_STRING );
	}

	void cCONNECT::operator ()()
	{
		ClientModule::getSingleton().ipAddress = ARG_S(0);
		ClientModule::getSingleton().initClient();
	}


	void clientCommunicate( void *dummy )
	{
		while( ClientModule::getSingleton().mbConnected )
		{
			NetworkMessage nMsg;
			int iSize = recv( ClientModule::getSingleton().ConnectSocket, (char *)&nMsg, sizeof( NetworkMessage ) - sizeof(int)*2, 0 );

			if( iSize > 0 )
			{
				nMsg.nSize = iSize - sizeof(int)*2;
				nMsg.nUser = 0; // For Now all we can deduce is that the server is sending us messages.
				// Beyond that who knows.
				if( WaitForSingleObject( ClientModule::getSingleton().mClientDataMutex, INFINITE ) == WAIT_OBJECT_0 )
				{

					ClientModule::getSingleton().mMessages.push_back( nMsg );
					ReleaseMutex( ClientModule::getSingleton().mClientDataMutex );					
				}
			} else if( iSize == 0 )
			{
				ClientModule::getSingleton().mbConnected = false;
				console( "Connection to Server Lost" );
			} else
			{
				ClientModule::getSingleton().mbConnected = false;
				std::cout << "Connection Lost..." << iSize << std::endl;
			}

		}
	}

	int ClientModule::initClient()
	{
		if( !mbConnected )
		{

			struct sockaddr_in my_addr;
			iResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
			if( iResult != 0 )
			{
				console( "Client: WSAStartup failed" );
				return 0;
			}

			memset( &hints, 0, sizeof( hints ) );
			hints.ai_family = AF_INET;
			hints.ai_socktype = SOCK_STREAM;
			hints.ai_protocol = IPPROTO_TCP;

			my_addr.sin_family = AF_INET;
			my_addr.sin_port = htons( atoi(DEFAULT_PORT) );
			my_addr.sin_addr.s_addr = inet_addr( ipAddress.c_str() );

			/*

			// 11001 Jim's error
			iResult = getaddrinfo( ipAddress.c_str(), DEFAULT_PORT, &hints, &result );
			if( iResult != 0 )
			{
				console( "Client: getaddrinfo failed" );
				console( gai_strerror(WSAGetLastError()) );
				console( "" );
				//std::cout << (int)WSAGetLastError() << std::endl;
				WSACleanup();
				return 0;
			}

			*/

			ConnectSocket = INVALID_SOCKET;
			//ptr = result;
			ConnectSocket = socket( AF_INET, SOCK_STREAM, 0 );
			if( ConnectSocket == INVALID_SOCKET )
			{
				console( "Client: error at socket()" );
				console( gai_strerror(WSAGetLastError()) );
				console( "" );
				freeaddrinfo( result );
				WSACleanup();
				return 0;
			}

			iResult = connect( ConnectSocket, (sockaddr*)&my_addr, sizeof(sockaddr) );
			if( iResult == SOCKET_ERROR )
			{
				console( "Client: Unable to Connect" );
				console( gai_strerror(WSAGetLastError()) );
				console( "" );
				closesocket( ConnectSocket );
				ConnectSocket = INVALID_SOCKET;
				return 0;
			}

			freeaddrinfo( result );

			mClientDataMutex = CreateMutex( 0, 0, "CLIENTDATAMUTEX" );
			ReleaseMutex( mClientDataMutex );

			mbConnected = true;

			_beginthread( clientCommunicate, 0, 0 );
					

		}
		
		return 1;
	}

	int ClientModule::initialize()
	{
		ADDCOMMAND( cCONNECT );
		nCurrMessageNumber = 0;
		mbConnected = false;
		return 1;
	}

	int ClientModule::run()
	{
		if( WaitForSingleObject( mClientDataMutex, INFINITE ) == WAIT_OBJECT_0 )
		{	
			// Currently we're just hoping not to drop any messages :)
			for( std::vector<NetworkMessage>::iterator i = mMessages.begin(); i != mMessages.end(); i++ )
			{
				if( processMessage )
					(*processMessage)( *i );
				else
					console( "Error: No Client Mesage Processor Given" );
			}

			mMessages.clear();

			ReleaseMutex( mClientDataMutex );					
		}
		return 1;
	}
	
	using namespace std;
	
	void ClientModule::sendMessage( NetworkMessage nMsg )
	{

		//std::cout << "Message Type: " << nMsg.nMsgType << std::endl;
		nMsg.nMsgNumber = nCurrMessageNumber++;
		//cout << "mNum: " << nMsg.nMsgNumber << " Type: " << nMsg.nMsgType << " Size: " << nMsg.nSize << " User: " << nMsg.nUser << endl;
		if( nMsg.nSize < 0 )
			nMsg.nSize = 0;
		if( nMsg.nSize > PACKET_BUFFER_LENGTH )
			nMsg.nSize = PACKET_BUFFER_LENGTH;

		send( ConnectSocket, (char *)&nMsg, nMsg.nSize+sizeof(int)*2, 0 );
		// TODO: Find out a way so we don't have to use Sleep(...)
		// I Don't know what the problem is.  I shall research
		// Oh Winsock.
		Sleep( 1 );
	}


	void ClientModule::sendMessage( int nMessage, const char *pBuffer, int nSize )
	{
		if( nSize > PACKET_BUFFER_LENGTH )
		{
			sendMessage( GM_MSG_BEGIN );
			int sizeRemaining = nSize;
			
			float packets = (float)nSize/PACKET_BUFFER_LENGTH;
			if( packets != (float)((int)packets) )
				packets = (int)packets+1;

			for( int i = 0; i < packets; i++ )
			{
				NetworkMessage nMsg;
				nMsg.nMsgType = nMessage;
				if( sizeRemaining > PACKET_BUFFER_LENGTH )
					nMsg.nSize = PACKET_BUFFER_LENGTH;
				else
					nMsg.nSize = sizeRemaining;
			
				
				memcpy( &nMsg.cBuffer, pBuffer + (PACKET_BUFFER_LENGTH)*i, nMsg.nSize );	
				//cout << "Buffer: " << nMsg.cBuffer << endl;
				sendMessage( nMsg );
				sizeRemaining -= PACKET_BUFFER_LENGTH;			
				
			}
			sendMessage( GM_MSG_END );		

		} else {
			NetworkMessage nMsg;
			memcpy( &nMsg.cBuffer, pBuffer, nSize );

			nMsg.nSize = nSize;
			nMsg.nMsgType = nMessage;
			sendMessage( nMsg );
		}


	}




}